global comprimir_archivo
;int comprimir_archivo ( char *archivo_entrada, char *archivo_salida )

extern fopen
extern fseek
extern ftell
extern malloc
extern calloc
extern fread
extern fclose
extern fwrite
extern free
extern printf
extern rewind
extern comprimir_buffer

%define p_archivo_ent ebp+8
%define p_archivo_sal ebp+12
%define pp_dst_buffer ebp-4
%define pp_dst_size  ebp-8
%define pp_tabla_codigos ebp-12
%define long_codificacion ebp-16

;estas variables es para guardar los ptr para liberar la memoria despues
%define ptr_buffer_lectura ebp-20
%define ptr_codificacion_t ebp-24

%define oc2_header  ebp-40
%define cod_error ebp-44
%define handle_in ebp-48
%define handle_out ebp-52

%define offset_tam_original 0
%define offset_long_codificacion 4
%define offset_cant_codigos 8
%define offset_offset_codificacion 12

; esto corresponde a codificacion_t
%define offset_simbolo 0
%define offset_codigo 1
%define offset_long_codigo 5

; esto corresponde a codigo_t
%define offset_codigo_t_codigo 0
%define offset_codigo_t_codigo_rev 4
%define offset_codigo_t_long_codigo 8
%define offset_codigo_t_valido 12

%define null 0
%define offset_cero 0
%define SEEK_END 2
%define error_fseek -1
%define ok_fclose 0
%define error_comprimir_buffer -1
%define sizeof_codificacion_t 	9
%define cant_simbolos	256
%define sizeof_header_oc2 16
%define sizeof_codigo_t 16
%define mascara_bits 00000007h
%define cod_ok 1
%define cod_not_ok 0


section .data
formato_open_read:	db	'rb',0
formato_open_write:	db	'wb',0
error_msg_fopen_in:	db	'ERROR: No se pudo abrir el archivo %s',10,0
error_msg_fseek:	db	'ERROR: Al llamar a fseek',10,0
error_msg_ftell:	db	'ERROR: Al llamar a ftell',10,0
error_msg_malloc:	db	'ERROR: Al llamar a malloc',10,0
error_msg_fwrite:	db	'ERROR: Al llamar a fwrite',10,0
error_fread_in:		db	'ERROR: Al leer el archivo de entrada',10,0
error_msg_comp_buffer:	db	'ERROR: Al realizar la compresion',10,0
error_fclose:		db	'ERROR: Al cerrar el archivo',10,0
error_msg_fopen_out:	db	'ERROR: No se pudo crear el archivo %s',10,0

section .text

comprimir_archivo:
	;enter 52,0	; 7 var locales (6 de 4 bytes y 1 de 32 bytes de tipo oc2_header)
	push ebp
	mov ebp,esp
	sub esp,52	; uso esta forma para probar con valgrind.
	
	push ebx
	push esi
	push edi

	xor eax,eax
	mov dword [ptr_buffer_lectura], null
	mov dword [ptr_codificacion_t], null
	mov dword [pp_dst_buffer], null
	mov dword [pp_tabla_codigos], null
	mov dword [handle_in],null
	mov dword [handle_out],null
	mov dword [cod_error],cod_ok			; inicializamos en ok		
	
	push dword formato_open_read
	push dword [p_archivo_ent]
	call fopen		; abrimos el archivo de entrada
	add esp,8
	
	mov [handle_in],eax	; guardamos el handle para cerrarlo en caso de error.
	
	cmp eax,null
	jne _llamar_fseek
	
	push dword [p_archivo_ent]
	push dword error_msg_fopen_in
	call printf
	add esp,8	
	mov dword [cod_error], cod_not_ok
	jmp _liberar_mem_fin		
	
_llamar_fseek:
	mov  ebx, eax		; ebx guarda el handler al archivo
	push dword SEEK_END
	push dword offset_cero
	push ebx
	call fseek
	add esp,12
	
	cmp eax, error_fseek
	jne _llamar_ftell 
	
	push dword error_msg_fseek
	call printf
	add esp,4
	mov dword [cod_error], cod_not_ok
	jmp _liberar_mem_fin

_llamar_ftell:

	push ebx
	call ftell
	add esp,4
	
	cmp eax, error_fseek
	jne _validar_archivo_vacio 
	
	push dword error_msg_ftell
	call printf
	add esp,4
	mov dword [cod_error], cod_not_ok
	jmp _liberar_mem_fin
	
_validar_archivo_vacio:
	cmp eax,0		; Comprobamos si es vacio.
	jne _pedir_mem_buffer
	; si es vacio, generamos un header vacio. No habría códigos ni nada para codificar.
	
	push dword [p_archivo_sal]
	call _generar_archivo_vacio
	add esp,4
	mov [cod_error], eax
	jmp _liberar_mem_fin
	
_pedir_mem_buffer:	
	mov esi, eax		; en esi guardamos la longitud.
	push dword esi
	call malloc
	add esp,4
	mov edi, eax		; en edi guardamos el ptr a buffer pedido para leer
	mov [ptr_buffer_lectura],eax   ; guardamos en la var local, para liberar al final.
	cmp edi, null
	jne _rewind
	
	push dword error_msg_malloc
	call printf
	add esp,4
	mov dword [cod_error], cod_not_ok
	jmp _liberar_mem_fin
	
_rewind:
	push dword ebx
	call rewind		; es una rutina void
	add esp,4
	
_read_file_in:
	push ebx		;handler
	push dword 1		;1 memoria
	push esi		;size del archivo
	push edi		;ptr al buffer
	call fread
	add esp,16

	cmp eax,1		; si fread leyo bien, deberia devolver 1
	je _close_file_in
	
	push dword error_fread_in
	call printf
	add esp,4
	
	mov dword [cod_error], cod_not_ok
	jmp _liberar_mem_fin
	
_close_file_in:	
	push ebx
	call fclose
	add esp,4
	
	mov dword [handle_in], null  ; actualizamos el handle para no cerrar dos veces el archivo
	
	cmp eax,ok_fclose
	je _llamar_comprimir_buffer
	
	push dword error_fclose
	call printf
	add esp,4
	mov dword [cod_error], cod_not_ok
	jmp _liberar_mem_fin

_llamar_comprimir_buffer:
	
	lea eax, [long_codificacion]
	push eax
	lea eax, [pp_tabla_codigos]
	push eax
	lea eax, [pp_dst_size]
	push eax
	lea eax, [pp_dst_buffer]
	push eax		
	push esi		;size buffer
	push edi		;ptr al buffer	

	call comprimir_buffer
	add esp,24
	cmp eax, error_comprimir_buffer
	jne _crear_buffer_codificacion_t
	
	mov dword [pp_dst_buffer],0	; si hubo error asumimos que no hay memoria para liberar
	mov dword [pp_tabla_codigos],0	; si hubo error asumimos que no hay memoria para liberar
	push dword error_msg_comp_buffer
	call printf
	add esp,4
	mov dword [cod_error], cod_not_ok
	jmp _liberar_mem_fin
	
	
_crear_buffer_codificacion_t:

	
	mov eax, sizeof_codificacion_t
	shl eax, 8	; multiplicamos por 256
	push eax
	call malloc
	add esp,4
	cmp eax, 0
	jne _llenar_buffer_codificacion
	
	push dword error_msg_malloc
	call printf
	add esp,4	
	mov dword [cod_error], cod_not_ok
	jmp _liberar_mem_fin
	
_llenar_buffer_codificacion:	
	mov [ptr_codificacion_t],eax	; Guardamos el ptr a el array de codificacion_t
	
	push esi	; resguardamos esi para usar en un ciclo
	
	mov esi, eax	; En esi tenemos un ptr al array de codificacion_t
	mov edi, [pp_tabla_codigos]   ; En edi tenemos el ptr a la tabla de codigos.
	xor ebx, ebx	; ebx lo usamos para contador de ciclo
	
_ciclo:

	mov edx,[edi+offset_codigo_t_valido]

	cmp edx,0
	jne _cargar_codigo
	jmp _incremento
	
_cargar_codigo:

	mov edx,edi
	
	mov [esi+offset_simbolo],bl	; escribimos el simbolo
	mov eax,[edx+offset_codigo_t_codigo]
	mov [esi+offset_codigo],eax
	mov eax,[edx+offset_codigo_t_long_codigo]
	mov [esi+offset_long_codigo], eax
	add esi, sizeof_codificacion_t	; apuntamos esi al siguiente registro.


_incremento:

	add edi,sizeof_codigo_t
	
	inc ebx
	cmp ebx,cant_simbolos
	je _calcular_num_codigos 
	jmp _ciclo

_calcular_num_codigos:


	mov eax, esi			; esi apunta al final del array
	sub eax, [ptr_codificacion_t]	; en eax tenemos los bytes ocupados por el array
	mov edi, eax			; en edi guardamos los bytes ocupados realmente.
	
	xor edx,edx
	mov ebx, sizeof_codificacion_t
	div ebx				; en eax queda el nro_de_codigos usados.

	; escribimos el header del archivo .oc2 en memoria	
	lea edx, [oc2_header]
	mov [edx+offset_cant_codigos], eax
	
	add edi, sizeof_header_oc2   ; en edi ahora tenemos el header+codigos
	mov [edx+offset_offset_codificacion],edi
	
	mov esi, [long_codificacion]
	mov [edx + offset_long_codificacion], esi
	
	pop esi		; restauramos esi (Tenia el tam. original)

	mov [edx+offset_tam_original], esi
	

	; creamos el archivo de salida, dado que ya tenemos todos los datos para grabar	
	
	push dword formato_open_write
	push dword [p_archivo_sal]
	call fopen		; creamos o truncamos el archivo de salida
	add esp,8
	mov [handle_out], eax	; Guardamos el handle para liberarlo despues.	
	cmp eax,null
	jne _volcar_header_oc2
	
	push dword [p_archivo_sal]
	push dword error_msg_fopen_out
	call printf
	add esp,8
	mov dword [cod_error], cod_not_ok
	jmp _liberar_mem_fin	
	
_volcar_header_oc2:
	; volcalmos el header
	mov esi, eax		; En esi ahora guardamos el handler
	push esi
	push dword 1
	push dword sizeof_header_oc2
	lea edx, [oc2_header]
	push edx
	call fwrite
	add esp,16
	
	cmp eax, 1
	je _sin_error_fwrite_header
	
	push dword error_msg_fwrite
	call printf
	add esp,4
	
	mov dword [cod_error], cod_not_ok
	jmp _close_file_out
	
_sin_error_fwrite_header:
	
	; volcamos la tabla de codificacion_t
	push esi
	push dword 1
	lea edx, [oc2_header]
	mov eax, [edx+offset_offset_codificacion]
	sub eax, sizeof_header_oc2
	push eax
	push dword [ptr_codificacion_t]
	call fwrite
	add esp,16
	
	cmp eax, 1
	je _sin_error_fwrite_codificacion_t

	push dword error_msg_fwrite
	call printf
	add esp,4
	
	mov dword [cod_error], cod_not_ok
	jmp _close_file_out	

_sin_error_fwrite_codificacion_t:

	; volcamos los datos comprimidos
	mov eax, [long_codificacion] 	; esta en bits
	mov edx, eax
	and edx, mascara_bits
	cmp edx,0
	je _pasar_a_byte
	add eax,8			; excedemos en 8 bits
_pasar_a_byte:
	shr eax, 3			; dividimos por 8
	; tenemos en eax la cantidad de bytes en lo que entra lo comprimido
	push esi
	push dword 1
	push eax
	mov edx, [pp_dst_buffer]
	push edx
	call fwrite
	add esp,16
	
	cmp eax,1
	je _close_file_out

	push dword error_msg_fwrite
	call printf
	add esp,4
	
	mov dword [cod_error], cod_not_ok
		
_close_file_out:
	mov esi,[handle_out]	; Lo asignamos porque no sabemos de donde venimos.
	push esi
	call fclose
	add esp,4

_liberar_mem_fin:
	; Aquí liberamos todo lo pedido para no perder memoria.
	; Cualquier puntero que no este en null, sera liberado con free
	
	mov eax, [handle_in]
	cmp eax, null
	je _liberar_dst_buffer
	push eax
	call fclose		; cerramos el archivo si quedo abierto
				; Por ejemplo si hay error en alguna llamada a rutina.
	add esp,4

_liberar_dst_buffer:
	
	mov eax, [pp_dst_buffer]
	cmp eax,null
	je _liberar_tabla_codigos
	push eax
	call free
	add esp,4
	
_liberar_tabla_codigos:			
	mov eax, [pp_tabla_codigos]
	cmp eax,null
	je _liberar_buffer_lectura
	push eax
	call free
	add esp,4
	
_liberar_buffer_lectura:	
	mov eax, [ptr_buffer_lectura]
	cmp eax,null
	je _liberar_ptr_codificacion_t
	push eax
	call free
	add esp,4
	
_liberar_ptr_codificacion_t:
	mov eax, [ptr_codificacion_t]
	cmp eax,null
	je fin
	
	push eax
	call free
	add esp,4	

fin:
	mov eax, [cod_error]
	pop edi
	pop esi
	pop ebx
	leave	

	ret

;int _generar_archivo_vacio(char *archivo_salida)
%define p_nombre_archivo_salida ebp+8
_generar_archivo_vacio:
	push ebp
	mov ebp,esp
	push ebx
	push esi
	push edi
	
	mov ebx, cod_ok
	
	push dword formato_open_write
	push dword [p_nombre_archivo_salida]
	call fopen		; creamos o truncamos el archivo de salida
	add esp,8
	
	cmp eax, null
	jne _escribir_header_vacio
	mov ebx, cod_not_ok
	jmp _fin_generar_archivo
	
_escribir_header_vacio:
	mov esi, eax		; en esi guardamos el handler
	mov edx, sizeof_header_oc2  ; en edx el sizeof del header
		
	push edx
	push dword 1
	call calloc
	add esp,8
	
	cmp eax, null
	jne _buffer_header_vacio_no_null
	mov ebx, cod_not_ok
	jmp _close_file_header_vacio
	
_buffer_header_vacio_no_null:
	
	mov edi,eax		; en edi ponemos el buffer
	
	mov edx, sizeof_header_oc2 
	mov dword [edi+offset_offset_codificacion], edx
	
	push esi			; handler
	push dword 1			; nmem
	push edx			; sizeof header
	push edi			; ptr al buffer
	call fwrite
	add esp,16	
	
	cmp eax, 1
	je _liberar_buffer_header_vacio
	mov ebx, cod_not_ok
	
_liberar_buffer_header_vacio:	
	
	push edi
	call free
	add esp,4

_close_file_header_vacio:	
	push esi
	call fclose
	add esp,4
	
	
_fin_generar_archivo:

	mov eax, ebx	; devolvemos en eax el error	
	pop edi
	pop esi
	pop ebx
	leave
	ret
	
